package cn.btks.commonserver.aspect.log;

import cn.btks.commonserver.pojo.SystemLog;
import cn.btks.commonserver.rabbitmq.RabbitProducer;
import cn.btks.commonserver.result.SystemLogEnum;
import cn.btks.commonserver.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

/**
 * @Author lijun
 * @Date 2021/1/4 20:23
 */
@Component
@Aspect
@Slf4j
public class SystemLogAspect {
    
    @Autowired
    RabbitProducer rabbitProducer;

    //切点
    @Pointcut("@annotation(cn.btks.commonserver.valid.SystemLog)")
    public void logPointcut(){}

    //前置通知
    @Before("logPointcut()")
    public void beforeLog(JoinPoint joinPoint) throws Exception {
        //系统操作日志
        SystemLog sysLog = wrapSysLog(joinPoint);
        log.info("Log值："+sysLog);
        //操作日志发送到mq队列
        rabbitProducer.sendMsg(sysLog);
    }

    //异常通知
    @AfterThrowing(value = "logPointcut()", throwing = "e")
    public void throwingLog(JoinPoint joinPoint, Exception e) throws Exception {
        //异常日志
        SystemLog sysLog = wrapSysLog(joinPoint);
        sysLog.setOperationName(SystemLogEnum.THROW_LOG.getType());
        sysLog.setLogMessage(sysLog.getLogMessage()+"==="+e);
        sysLog.setCreatDateTime(new Date());
        log.info("异常Log值："+sysLog);
        //操作日志发送到mq队列
        rabbitProducer.sendMsg(sysLog);
    }

    /**
     * 封装SysLog对象
     * @param joinPoint
     * @return
     */
    public SystemLog wrapSysLog(JoinPoint joinPoint) throws Exception {
        //获取请求响应对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        SystemLog sysLog = new SystemLog();

        //获取方法全路径
        String methodName = signature.getDeclaringTypeName()+"."+signature.getName();
        Method method = signature.getMethod();
        //获取方法参数
        LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
        if (args != null && paramNames != null) {
            String params = "";
            for (int i = 0; i < args.length; i++) {
                params += " " + paramNames[i] + ": " + args[i];
            }
            sysLog.setObjMessage(params);
        }
        //获取注解参数值
        cn.btks.commonserver.valid.SystemLog systemLog = signature.getMethod().getAnnotation(cn.btks.commonserver.valid.SystemLog.class);
        //从header取出token
        String token = request.getHeader("token");
        if(!StringUtils.isEmpty(token)){
            Map<String, Object> tokenInfo = JwtUtil.getTokenInfo(request, JwtUtil.jwtsSecret);
            if(MapUtils.isNotEmpty(tokenInfo)){
                sysLog.setUserId(Long.valueOf(tokenInfo.get("id").toString()));
                sysLog.setUserName(tokenInfo.get("username").toString());
                sysLog.setRoleName(tokenInfo.get("roles").toString());
                sysLog.setUnitId(Long.valueOf(tokenInfo.get("unitId").toString()));
                sysLog.setUnitName(tokenInfo.get("unitName").toString());
            }
        }
        if (!StringUtils.isEmpty(systemLog.type())){
            sysLog.setOperationName(systemLog.type().getType());
        }
        if (!StringUtils.isEmpty(systemLog.moduleMessage())){
            sysLog.setModule(systemLog.moduleMessage().getSource());
        }
        sysLog.setCreatDateTime(new Date());
        sysLog.setLogMessage(methodName);
        return sysLog;
    }
}
